
#include<stdio.h>
#include<string.h>
#include"DES.h"
#include <stdlib.h>
#include<time.h>

typedef struct {
    char *plain_file;
    char *key_file;
    char *vector_file;
    char *mode;
    char *cipher_file;
} parameters;




int main(int argc, char *argv[]) {
    parameters param;
    int i = 1;
    param.plain_file = "des_plain.txt";
    param.cipher_file = "cipher_file.txt";
    param.key_file = "des_key.txt";
    param.vector_file = "des_iv.txt";
    param.mode = "ECB";
    while (i < argc) {
        if (!strcmp(argv[i], "-p"))
            param.plain_file = argv[i + 1];
        else if (!strcmp(argv[i], "-k"))
            param.key_file = argv[i + 1];

        
        else if (!strcmp(argv[i], "-m"))
            param.mode = argv[i + 1];
        else if (!strcmp(argv[i], "-c"))
            param.cipher_file = argv[i + 1];
        else if (!strcmp(argv[i], "-v"))
            param.vector_file = argv[i + 1];
        i += 2;
    }
    if (!strcmp(param.mode, "ECB"))
        ECB(param.plain_file, param.key_file, param.cipher_file);

    else if (!strcmp(param.mode, "CBC"))
        CBC(param.plain_file, param.key_file, param.cipher_file, param.vector_file);
    else if (!strcmp(param.mode, "CFB"))
        CFB(param.plain_file, param.key_file, param.cipher_file, param.vector_file);
    else if (!strcmp(param.mode, "OFB"))
        OFB(param.plain_file, param.key_file, param.cipher_file, param.vector_file);
    else
        printf("Wrong mode!Please try again!");
    printf("Encrypt successfully!\n");
    time_cost();
    getchar();

    return 0;
}

void time_cost() {
    parameters param;
    param.plain_file = "des_plain.txt";
    param.cipher_file = "cipher_file.txt";
    param.key_file = "des_key.txt";
    param.vector_file = "des_iv.txt";
    param.mode = "OFB";
    clock_t start, end;
    int i = 0;
//	start =clock();
//	for(;i<13000;i++)
//	{
//		ECB(param.plain_file,param.key_file,"cipher_file_ECB_time_test.txt");
//	}
//	end=clock();
//	printf("ECB costs %d  ms\n",(int)(end-start));

//	start =clock();
//	for(;i<13000;i++)
//	{
//		CBC(param.plain_file,param.key_file,"cipher_file_CBC_time_test.txt",param.vector_file);
//	}
//	end=clock();
//	printf("CBC costs %d  ms\n",(int)(end-start));

//	start =clock();
//	for(;i<13000;i++)
//	{
//		CFB(param.plain_file,param.key_file,"cipher_file_CFB_time_test.txt",param.vector_file);
//	}
//	end=clock();
//	printf("CFB costs %d  ms\n",(int)(end-start));

    start = clock();
    for (; i < 13000; i++) {
        OFB(param.plain_file, param.key_file, "cipher_file_OFB_time_test.txt", param.vector_file);
    }
    end = clock();
    printf("OFB costs %d  ms", (int) (end - start));

}


void permutation(char *In, char *Out, int n, char *P) {
    int i = 0;
    for (i = 0; i < n; i++)
        *(Out + i) = *(In + *(P + i) - 1);
    *(Out + i) = '\0';
}




int ECB(char *plain_file, char *key_file, char *cipher_file) {
    char plain_bin[65] = {0};
    char key_bin[65] = {0};
    char cipher_bin[65] = {0};
    char plain_hex[17] = {0};
    char cipher_hex[17] = {0};
    FILE *pFile1 = fopen(plain_file, "r");

    FILE *pFile2 = fopen(cipher_file, "a");
    if (pFile2 == NULL) {
        printf("Error to open %s", cipher_file);
        exit(0);
    }
    if (pFile1 == NULL) {
        printf("Error to open %s", plain_file);
        exit(0);
    }
    read_settings(key_file, key_bin);
    int flag = fread(plain_hex, sizeof(char), 16, pFile1);
    while (flag) {
        Hex2Bin(plain_hex, plain_bin, 64);
        char *input = plain_bin;
        char *K = key_bin;
        char input_after_permutation[65], L[33], R[33], SK[16][49];
        DES_encode(K, SK, input, input_after_permutation, L, R, cipher_bin);
        Bin2Hex(cipher_bin, cipher_hex, 16);
        fwrite(cipher_hex, sizeof(char), 16, pFile2);
        flag = fread(plain_hex, sizeof(char), 16, pFile1);
    }
    fclose(pFile1);
    fclose(pFile2);
    return 0;
}

int CBC(char *plain_file, char *key_file, char *cipher_file, char *vector_file) {
    FILE *pFile1 = fopen(plain_file, "r");
    FILE *pFile2 = fopen(cipher_file, "a");
    char plain_bin[65] = {0};
    char key_bin[65] = {0};
    char cipher_bin[65] = {0};
    char vector_bin[65] = {0};
    char plain_hex[17] = {0};
    char cipher_hex[17] = {0};
    char temp[65] = {0};
    int flag = fread(plain_hex, sizeof(char), 16, pFile1);
    read_settings(key_file, key_bin);
    read_settings(vector_file, vector_bin);
    Hex2Bin(plain_hex, plain_bin, 64);
    strcpy(temp, plain_bin);
    XOR(temp, vector_bin, 64, plain_bin);
    while (flag) {
        char *K = key_bin;
        char *input = plain_bin;
        char input_after_permutation[65], L[33], R[33], SK[16][49];
        DES_encode(K, SK, input, input_after_permutation, L, R, cipher_bin);
        Bin2Hex(cipher_bin, cipher_hex, 16);
        fwrite(cipher_hex, sizeof(char), 16, pFile2);
        flag = fread(plain_hex, sizeof(char), 16, pFile1);
        Hex2Bin(plain_hex, plain_bin, 64);
        strcpy(temp, plain_bin);
        XOR(temp, cipher_bin, 64, plain_bin);
    }
    fclose(pFile1);
    fclose(pFile2);
    return 0;
}

void move(char *reg, char *cipher, int t) {
    int j, i = 0;
    while (i < strlen(reg) - t) {
        reg[i] = reg[i + t];
        i++;
    }
    for (j = 0; j < t; j++)
        reg[i + j] = cipher[j];
}

int CFB(char *plain_file, char *key_file, char *cipher_file, char *vector_file) {
    char reg_bin[65] = {0};
    char key_bin[65] = {0};
    char output[65] = {0};
    char vector_bin[65] = {0};
    char cipher_bin[9] = {0};
    char plain_bin[9] = {0};
    char plain_hex[3] = {0};
    char cipher_hex[3] = {0};
    char intercepted_output[9] = {0};
    read_settings(key_file, key_bin);
    read_settings(vector_file, vector_bin);
    FILE *pFile2 = fopen(cipher_file, "a");
    FILE *pFile1 = fopen(plain_file, "r");
    int flag = fread(plain_hex, sizeof(char), 2, pFile1);
    Hex2Bin(plain_hex, plain_bin, 8);
    strcpy(reg_bin, vector_bin);
    while (flag) {
        char *K = key_bin;
        char *input = reg_bin;
        char input_after_permutation[65], L[33], R[33], SK[16][49];
        DES_encode(K, SK, input, input_after_permutation, L, R, output);
        strncpy(intercepted_output, output, 8);
        XOR(intercepted_output, plain_bin, 8, cipher_bin);
        Bin2Hex(cipher_bin, cipher_hex, 2);
        fwrite(cipher_hex, sizeof(char), 2, pFile2);
        move(reg_bin, cipher_bin, 8);
        flag = fread(plain_hex, sizeof(char), 2, pFile1);
        Hex2Bin(plain_hex, plain_bin, 8);
    }
    fclose(pFile1);
    fclose(pFile2);
    return 0;
}

int OFB(char *plain_file, char *key_file, char *cipher_file, char *vector_file) {
    char reg_bin[65] = {0};
    char key_bin[65] = {0};
    char output[65] = {0};
    char vector_bin[65] = {0};
    char cipher_bin[9] = {0};
    char plain_bin[9] = {0};
    char plain_hex[3] = {0};
    char cipher_hex[3] = {0};
    char intercepted_output[9] = {0};
    read_settings(key_file, key_bin);
    read_settings(vector_file, vector_bin);
    FILE *pFile2 = fopen(cipher_file, "a");
    FILE *pFile1 = fopen(plain_file, "r");
    int flag = fread(plain_hex, sizeof(char), 2, pFile1);
    Hex2Bin(plain_hex, plain_bin, 8);
    strcpy(reg_bin, vector_bin);
    while (flag) {
        char *K = key_bin;
        char *input = reg_bin;
        char input_after_permutation[65], L[33], R[33], SK[16][49];
        DES_encode(K, SK, input, input_after_permutation, L, R, output);
        strncpy(intercepted_output, output, 8);
        XOR(intercepted_output, plain_bin, 8, cipher_bin);
        Bin2Hex(cipher_bin, cipher_hex, 2);
        fwrite(cipher_hex, sizeof(char), 2, pFile2);
        move(reg_bin, intercepted_output, 8);
        flag = fread(plain_hex, sizeof(char), 2, pFile1);
        Hex2Bin(plain_hex, plain_bin, 8);
    }
    fclose(pFile1);
    fclose(pFile2);
    return 0;
}

void XOR(char *In1, char *In2, int n, char *Out) {
    int i = 0;
    for (i = 0; i < n; i++)
        *(In1 + i) != *(In2 + i) ? *(Out + i) = '1' : *(Out + i) = '0';
}



void cyclic_left_shift(char *In, char *Out, int n, int s) {
    int i = 0;
    for (i = 0; i < n; i++)
        *(Out + i) = *(In + (s + i) % n);
    *(Out + i) = '\0';
}


void generate_SK(char *K, char (*SK)[49]) {
    char out[57], C[57], D[29], temp_string[29], Ci_Di[57];
    int j = 0;
    permutation(K, out, 56, *PC_1);
    strcpy(C, out);
    strcpy(D, out + 28);
    for (j = 0; j < 16; j++) {
        cyclic_left_shift(C, temp_string, 28, move_time[j]);
        strcpy(C, temp_string);
        cyclic_left_shift(D, temp_string, 28, move_time[j]);
        strcpy(D, temp_string);
        strncpy(Ci_Di, C, 28);
        strncpy(Ci_Di + 28, D, 28);
        permutation(Ci_Di, *(SK + j), 48, *PC_2);
    }
}



void f_function(char *L, char *R, char *SK, int t) {
    int i, j;
    int row, col;
    char after_permutationE[49] = {0}, after_XOR[49] = {0}, after_S_box[33] = {0}, after_permutationP[33] = {
            0}, temp[33] = {0};
    permutation(R, after_permutationE, 48, *E);
    XOR(after_permutationE, SK, 48, after_XOR);
    for (i = 0; i < 8; i++){
        row = ((after_XOR[i * 6] - '0') << 1) + (after_XOR[i * 6 + 5] - '0');
        col = ((after_XOR[i * 6 + 1] - '0') << 3) + ((after_XOR[i * 6 + 2] - '0') << 2) +
              ((after_XOR[i * 6 + 3] - '0') << 1)
              + (after_XOR[i * 6 + 4] - '0');
        for (j = 3; j >= 0; j--)
            *(after_S_box + (i * 4 + 3 - j)) =
                    ((S_Box[i][row * 16 + col] >> j) & 1) + '0';

    }
    *(after_S_box + 32) = '\0';
    permutation(after_S_box, after_permutationP, 32, *P);
    strcpy(temp, R);
    XOR(L, after_permutationP, 32, R);
    strcpy(L, temp);
}


void Hex2Bin(char *str, char *dest, int times) {
    int res;

    char des[65] = {0};
    char reference[16][5] = {"0000", "0001", "0010", "0011", \
        "0100", "0101", "0110", "0111", \
        "1000", "1001", "1010", "1011", \
        "1100", "1101", "1110", "1111"};
    int i = 0;
    while (i < times / 4) {
        char num = str[i];
        switch (num) {
            case 'A':
                res = 10;
                break;
            case 'B':
                res = 11;
                break;
            case 'C':
                res = 12;
                break;
            case 'D':
                res = 13;
                break;
            case 'E':
                res = 14;
                break;
            case 'F':
                res = 15;
                break;
            default:
                res = (int) num - 48;
                break;
        }
        strcat(des, reference[res]);
        i++;
    }
    strncpy(dest, des, times);
}

void Bin2Hex(char *sSrc, char *sDest, int times) {
    char temp[17] = {0};
    int x = 0;
    for (int i = 0; i < times; i++) {
        x = 8 * (sSrc[i * 4] - '0');
        x += 4 * (sSrc[i * 4 + 1] - '0');
        x += 2 * (sSrc[i * 4 + 2] - '0');
        x += sSrc[i * 4 + 3] - '0';
        switch (x) {
            case 10:
                temp[i] = 'A';
                break;
            case 11:
                temp[i] = 'B';
                break;
            case 12:
                temp[i] = 'C';
                break;
            case 13:
                temp[i] = 'D';
                break;
            case 14:
                temp[i] = 'E';
                break;
            case 15:
                temp[i] = 'F';
                break;
            default:
                temp[i] = (char) (x + 48);
                break;
        }
    }
    strncpy(sDest, temp, times);
}

void read_settings(char *filename, char *dest) {
    FILE *pFile0 = fopen(filename, "r");
    char key_bin[65] = {0};
    char key_hex[17] = {0};
    fread(key_hex, sizeof(char), 16, pFile0);
    fclose(pFile0);
    Hex2Bin(key_hex, key_bin, 64);
    strcpy(dest, key_bin);
}

void DES_encode(char* K, char (*SK)[49], char* input, char *input_after_permutation, char* L, char* R, char* output){
    int i;
    generate_SK(K, SK);
    permutation(input, input_after_permutation, 64, *IP);
    strcpy(L, input_after_permutation);
    strcpy(R, input_after_permutation + 32);
    for (i = 0; i < 16; i++) {
        f_function(L, R, *(SK + i), i);
    }
    strncpy(input_after_permutation, R, 32);
    strncpy(input_after_permutation + 32, L, 32);
    permutation(input_after_permutation, output, 64, *C_IP);
}








